﻿#include "hsffmpeg.h"


HSFFmpeg::HSFFmpeg()
{

}
void HSFFmpeg::HS_FFmpeg_SetUrl(StreamType stype,QString url){
    this->HS_StreamUrl = url;
    PRTLOG()<<HS_StreamUrl;
    this->HS_StreamType = stype;
}

HSFFmpeg::~HSFFmpeg(){
  free(HS_YUV_Data);
  delete  HS_Control;
  avcodec_free_context(&HS_Video_CodecCtx);
  avformat_close_input(&HS_Video_FormatCtx);
  av_buffer_unref(&HS_Video_HWDevice_ctx);
}

void HSFFmpeg::HS_SDL2_Deinit(){
    SDL_DestroyWindow(HS_SDL2_VideoWidget);
    HS_SDL2_VideoWidget=nullptr;
    SDL_DestroyRenderer(HS_SDL2_VideoRender);
    HS_SDL2_VideoRender = nullptr;
    SDL_DestroyTexture(HS_SDL2_VideoTexture);
    HS_SDL2_VideoTexture= nullptr;
}
bool HSFFmpeg::HS_SDL2_Init(){

//    HS_SDL2_VideoWidget = SDL_CreateWindowFrom((void *)HS_QT_VideoLabel->winId());
    PRTLOG()<<HS_WindowRect.width<<"x"<<HS_WindowRect.height;
    HS_SDL2_VideoWidget = SDL_CreateWindow(nullptr,HS_WindowRect.pos_x+1,HS_WindowRect.pos_y+1,HS_WindowRect.width,HS_WindowRect.height,SDL_WINDOW_BORDERLESS);


    if(HS_SDL2_VideoWidget == nullptr){
        PRTLOG()<<"[SDL2]:SDL_CreateWindowFrom fail";
        return false;
    }
    int Redernum=0;
    Redernum=SDL_GetNumRenderDrivers();
    if(!Redernum){
        return false;
    }else{
        for(int i=0;i<Redernum;i++){
            SDL_RendererInfo xx;
            SDL_GetRenderDriverInfo(i,&xx);
            PRTLOG()<<"RenderName:"<<xx.name;
            PRTLOG()<<"RenderSize:"<<xx.max_texture_width<<"x"<<xx.max_texture_height;
            if(i==0){
                PRTLOG()<<"RenderHInt"<<SDL_GetHint(xx.name);
            }
        }
    }
    HS_SDL2_VideoRender = SDL_CreateRenderer(HS_SDL2_VideoWidget,-1,0);
    if(HS_SDL2_VideoRender==nullptr)
    {
        PRTLOG()<<"[SDL2]:SDL_CreateRenderer fail";
        return false;
    }
    HS_SDL2_VideoTexture = SDL_CreateTexture(HS_SDL2_VideoRender,SDL_PIXELFORMAT_NV12,SDL_TEXTUREACCESS_STREAMING,HS_WindowRect.width,HS_WindowRect.height);
    ///w,h is the video's width and height
    if(!HS_SDL2_VideoTexture){
        PRTLOG()<<"[SDL2]:SDL_CreateTexture fail";
        return false;
    }
    return true;
}


void HSFFmpeg::HS_FFmpeg_ReSws(){
    sws_freeContext(HS_Video_SwsCtx);
    HS_Video_SwsCtx=nullptr;
    avpicture_free(&HS_Video_Picture);
    PRTLOG()<<HS_WindowRect.width<<"x"<<HS_WindowRect.height;
    HS_Video_SwsCtx = sws_getContext(HS_Video_Width,HS_Video_Height,
                                     HS_Video_Codec->pix_fmts==nullptr?AV_PIX_FMT_NV12:*HS_Video_Codec->pix_fmts,
                                     HS_WindowRect.width,HS_WindowRect.height,AV_PIX_FMT_NV12,SWS_BILINEAR,nullptr,nullptr,nullptr);
    avpicture_alloc(&HS_Video_Picture,AV_PIX_FMT_NV12,HS_WindowRect.width,HS_WindowRect.height);

    }
bool HSFFmpeg::HS_FFmpeg_Init(){
    int ret=0;
    if(0!=avformat_open_input(&HS_Video_FormatCtx,this->HS_StreamUrl.toLocal8Bit().data(),nullptr,nullptr)){
        PRTLOG()<<"open"<<this->HS_StreamUrl<<"fail";
        return false;
    }
    if(0>avformat_find_stream_info(HS_Video_FormatCtx,nullptr))
    {
        PRTLOG()<<"avformat_find_stream_info fail";
        return false;
    }
    ret = av_find_best_stream(HS_Video_FormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &HS_Video_Codec, 0);
    if(ret <0){
        PRTLOG()<<"av_find_best_stream Video Decoder  fail";
        return false;
    }
    HS_Video_Index = ret;

    for (int i=0;;i++) {
        HS_Video_HWCodecConfig = avcodec_get_hw_config(HS_Video_Codec, i);
        if (!HS_Video_HWCodecConfig) {
            fprintf(stderr, "Decoder %s does not support device type %s.\n",
                    HS_Video_Codec->name, av_hwdevice_get_type_name(HS_HardDecoder_Type));
            return false;
        }
        if (HS_Video_HWCodecConfig->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
            HS_Video_HWCodecConfig->device_type == HS_HardDecoder_Type) {
            HS_Video_HWPix_fmt = HS_Video_HWCodecConfig->pix_fmt;
            break;
        }
  }
   if (!(HS_Video_CodecCtx = avcodec_alloc_context3(HS_Video_Codec))){
        PRTLOG()<<"avcodec_alloc_context3-->HS_Video_CodecCtx fail";
        return false;
   }
   HS_Video_Stream = HS_Video_FormatCtx->streams[HS_Video_Index];
   AVRational fmrt=av_stream_get_r_frame_rate(HS_Video_Stream);
   HS_Video_FrameRate = fmrt.num /fmrt.den;
   if(HS_Video_FrameRate >50)
       HS_Video_FrameRate=25;


   if (avcodec_parameters_to_context(HS_Video_CodecCtx, HS_Video_Stream->codecpar) < 0){
        PRTLOG()<<"fill the HS_Video_CodecCtx by the video->codecPar fail";
        return false;
   }
   HS_Video_CodecCtx->get_format = get_hw_format;

   if(av_hwdevice_ctx_create(&HS_Video_HWDevice_ctx, HS_HardDecoder_Type,NULL, NULL, 0)<0){
       PRTLOG()<< "Failed to create specified HW device.";
       return false;
   }
   HS_Video_CodecCtx->hw_device_ctx = av_buffer_ref(HS_Video_HWDevice_ctx);
   HS_Video_CodecCtx->hwaccel_flags |= AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH;
   if(avcodec_open2(HS_Video_CodecCtx, HS_Video_Codec, NULL) < 0){
           PRTLOG()<<"Failed to open codec for Video_stream";
           return false;
   }
   HS_Video_Width = HS_Video_CodecCtx->width;
   HS_Video_Height = HS_Video_CodecCtx->height;
   PRTLOG()<<"Video:"<<HS_Video_Width<<"x"<<HS_Video_Height;
   PRTLOG()<<"Widget:"<<HS_WindowRect.width<<"x"<<HS_WindowRect.height;

   HS_Video_SwsCtx = sws_getContext(HS_Video_Width,HS_Video_Height,
                                    HS_Video_Codec->pix_fmts==nullptr?AV_PIX_FMT_NV12:*HS_Video_Codec->pix_fmts,
                                    HS_WindowRect.width,HS_WindowRect.height,AV_PIX_FMT_NV12,SWS_BILINEAR,nullptr,nullptr,nullptr);
   avpicture_alloc(&HS_Video_Picture,AV_PIX_FMT_NV12,HS_WindowRect.width,HS_WindowRect.height);
   return true;
}

void HSFFmpeg::HS_FFmpeg_Play(){
    int ret=0;
//    PRTLOG()<<"xxxxxxx"<<HS_Video_FrameRate;
    AVPacket *HS_Video_AVpkt = av_packet_alloc();
    ret = av_read_frame(HS_Video_FormatCtx,HS_Video_AVpkt);
    if((ret==0) && (HS_Video_AVpkt->stream_index==HS_Video_Index) ){///Video Decode
        HS_FFmpeg_DecodeVideo_Play(HS_Video_CodecCtx,HS_Video_AVpkt);
    }
    av_packet_free(&HS_Video_AVpkt);
}

void HSFFmpeg::HS_FFmpeg_Set_WindowRect(int x,int y,int w,int h){
    HS_WindowRect.pos_x = x;
    HS_WindowRect.pos_y = y;
    HS_WindowRect.width = w;
    HS_WindowRect.height = h;
    HS_YUV_Data = (unsigned char*)malloc(sizeof(unsigned char)*w*h*3/2);
    HS_SDL2_VideoRect.x = 0;
    HS_SDL2_VideoRect.y = 0;
    HS_SDL2_VideoRect.w = w;
    HS_SDL2_VideoRect.h = h;
}

void HSFFmpeg::run(){
    HS_Control = new HS_PlayControl();
    connect(this->HS_Control,SIGNAL(timeout()),this,SLOT(HS_FFmpeg_Play()));
    HS_Control->setInterval(1000/HS_Video_FrameRate);
    HS_Control->start();
    this->exec();
}
int HSFFmpeg::HS_FFmpeg_DecodeVideo_Play(AVCodecContext *avctx, AVPacket *packet)
{
    AVFrame *frame = NULL, *sw_frame = NULL;
    AVFrame *tmp_frame = NULL;
    uint8_t *buffer = NULL;
    int size;
    int ret = 0;
    ret = avcodec_send_packet(avctx, packet);
    if (ret < 0) {
        PRTLOG()<<"Error during decoding err_code"<<AVERROR(EAGAIN);
        return ret;
    }
    while(1){
        if (!(frame = av_frame_alloc()) || !(sw_frame = av_frame_alloc())) {
            PRTLOG()<<"Can not alloc frame";
            goto fail;
        }
        ret = avcodec_receive_frame(avctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            av_frame_free(&frame);
            av_frame_free(&sw_frame);
            return 0;
        } else if (ret < 0) {
            PRTLOG()<<"Error while decoding:error_"<<ret;
            goto fail;
        }
        if (frame->format == HS_Video_HWPix_fmt) {
            /* retrieve data from GPU to CPU */
            if ((ret = av_hwframe_transfer_data(sw_frame, frame, 0)) < 0) {
                PRTLOG()<<"Error transferring the data to system memory";
                goto fail;
            }
            tmp_frame = sw_frame;
        } else
            tmp_frame = frame;
#if 0
        switch(frame->pict_type){
            case AV_PICTURE_TYPE_I:PRTLOG()<<"I_Frame";break;
            case AV_PICTURE_TYPE_P:PRTLOG()<<"P_Frame";break;
            case AV_PICTURE_TYPE_B:PRTLOG()<<"B_Frame";break;
            default:PRTLOG()<<"UNKnown_Frame";
        }
#endif
        sws_scale(HS_Video_SwsCtx,sw_frame->data,sw_frame->linesize,0,HS_Video_Height,HS_Video_Picture.data,HS_Video_Picture.linesize);
        HS_YUV_Data = (unsigned char *)malloc(HS_WindowRect.width*HS_WindowRect.height*3/2);
        memset(HS_YUV_Data,128,HS_WindowRect.width*HS_WindowRect.height*3/2);
#if 1
        memcpy(&HS_YUV_Data[0],HS_Video_Picture.data[0],HS_WindowRect.width*HS_WindowRect.height);
        memcpy(&HS_YUV_Data[HS_WindowRect.width*HS_WindowRect.height],HS_Video_Picture.data[1],HS_WindowRect.width*HS_WindowRect.height/2);
#else
        memcpy(&HS_YUV_Data[0],sw_frame->data[0],HS_WindowRect.width*HS_WindowRect.height);
        memcpy(&HS_YUV_Data[HS_WindowRect.width*HS_WindowRect.height],sw_frame->data[1],HS_WindowRect.width*HS_WindowRect.height/2);
#endif

        SDL_UpdateTexture(HS_SDL2_VideoTexture,&HS_SDL2_VideoRect,HS_YUV_Data,HS_WindowRect.width);
        SDL_RenderClear(HS_SDL2_VideoRender);
        SDL_RenderCopy(HS_SDL2_VideoRender,HS_SDL2_VideoTexture,nullptr,&HS_SDL2_VideoRect);
        SDL_RenderPresent(HS_SDL2_VideoRender);
        free(HS_YUV_Data);
fail:
    av_frame_free(&frame);
    av_frame_free(&sw_frame);
    av_freep(&buffer);
    if (ret < 0)
        return ret;
    }

}



